package top.mable.common.aop;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.mable.common.vo.Constant;
import top.mable.common.vo.RequestData;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.UUID;

@Aspect
@Component
@Slf4j
public class SystemLogAspect {

    /**
     * Controller层切点
     */
    @Pointcut("businessControllerAspect() || apiControllerAspect()")
    public void controllerAspect() {
    }

    @Pointcut("within(top.mable.payment.controller.*)")
    private void businessControllerAspect() {
    }

    @Pointcut("within(top.mable.adjust.controller.*)")
    private void apiControllerAspect() {
    }

    /**
     * 注解@xxlJob层切点
     */
    @Pointcut("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")
    public void xxlJobAspect() {
    }

    @Pointcut("execution(* top.mable.*.controller.*(..))")
    public void getOneAop(){
    }
    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     * 出参中塞入taceId
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("=========================请求开始=========================");
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            // 收到请求记录内容
            String url = request.getRequestURL().toString();
            String method = request.getMethod();
            String id = request.getRemoteAddr();

            log.info("url: {}, method: {}, id: {}", url, method, id);
            Enumeration<String> enums = request.getParameterNames();
            while (enums.hasMoreElements()) {
                String name = enums.nextElement();
                log.info("name:{},value:{}", name, request.getParameter(name));
            }
            // 打印出入参
            log.info("body: {}", JSONUtil.toJsonStr(joinPoint.getArgs()));

            /**
             * request 中设置 traceId
             */
            if (joinPoint.getArgs().length > 0 && joinPoint.getArgs()[0] instanceof RequestData) {
                request.setAttribute(Constant.TRACE_ID, ((RequestData) joinPoint.getArgs()[0]).getReqId());
            }
        } catch (Exception e) {
            //记录本地异常日志
            // log.error("==前置通知异常==");
            //log.error("异常信息:{}", e.getMessage());
        }

    }

    /**
     * @AfterReturning   没有 returning = "obj" 的时候，报错： Error creating bean with name 'sentinelBeanPostProcessor'
     * @param joinPoint
     * @param obj
     */
    @AfterReturning(value = "controllerAspect()",returning = "obj")
    public void doAfter(JoinPoint joinPoint, Object obj) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        log.info("{},{}:返回参数：{}",method.getDeclaringClass().getName(),method.getName(),JSONUtil.toJsonStr(obj));
    }


    /**
     * 测试注解在aop中的使用
     * ProceedingJoinPoint 必须是  Around
     * 使用MDC实现链路追踪
     * @param
     */
/*    @Around("controllerAspect()")
    public Object doServiceLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("serviceAspect执行service之前打个日志,参数：{}",JSONUtil.toJsonStr(joinPoint.getArgs()));
        MDC.put("X-B3-TraceId", UUID.randomUUID().toString());
        Object result = joinPoint.proceed();
        MDC.clear();
        return result;
    }*/

    @After("getOneAop()")
    public void testGetOne(){
        log.info("就只是想测试aop。。。。。。。");
    }


    @Around("xxlJobAspect()")
    public void beforMethod(ProceedingJoinPoint joinPoint){
        //往slf4j的MDC中添加traceId
        //log.info("xxlJobAspect-------------------------------------------");
        try {
            MDC.put("X-B3-TraceId", DigestUtils.md5Hex(UUID.randomUUID().toString()).substring(8, 24));
            joinPoint.proceed();
            MDC.remove("X-B3-TraceId");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //log.info("method :{} 修改traceId",joinPoint.getSignature());
    }
}
